package com.lee.boot.order.service.impl;

import com.lee.boot.order.entity.BuyInfo;
import com.lee.boot.order.mapper.BuyInfoMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lee.boot.order.entity.ProductInfo;
import com.lee.boot.order.mapper.ProductInfoMapper;
import com.lee.boot.order.service.ProductInfoService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ProductInfoServiceImpl extends ServiceImpl<ProductInfoMapper, ProductInfo> implements ProductInfoService {
    @Autowired
    ProductInfoMapper productInfoMapper;
    @Autowired
    BuyInfoMapper buyInfoMapper;

    @Override

    public ProductInfo getProductInfo(String productId) {
        ProductInfo info = productInfoMapper.getProductInfoByProductId(productId);
        return info;
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean buyProduct(String productId, Integer count) {
        /**
         * 单使用事物，2200次购买，4次超卖，耗时3分27秒
         */
        ProductInfo info = productInfoMapper.getProductInfoByProductId(productId);
        /**
         * 悲观锁 2200次购买，无错误数据，耗时5分32秒
         */
//        ProductInfo info = productInfoMapper.getProductInfoByProductIdPessimismLock(productId);
        if (null == info || count > info.getInventoryCount()) {
            return false;
        }
        int i = productInfoMapper.reduceProductInventory(productId, count);
//        int a = ThreadLocalRandom.current().nextInt(9,100);
//        if (a==97){
//            throw new Exception();
//        }
        BuyInfo buyInfo = initBuyInfo(productId, count, info);
        int i1 = buyInfoMapper.insertBuyInfo(buyInfo);
        return true;
    }

    /**
     * 不重入乐观锁，购买2200次，库存减少440，剩余库存1560，也就是说失败次数1560次。版本号441
     * 将事物隔离级别改为读写提交后库存减少650，剩余库存1350 版本号651
     *
     * @param productId
     * @param count
     * @return
     */
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public Boolean buyProductOptimismLock(String productId, Integer count) {
        ProductInfo info = productInfoMapper.getProductInfoByProductId(productId);
        if (null == info || count > info.getInventoryCount()) {
            return false;
        }
        int i = productInfoMapper.reduceProductInventoryOptimismLock(productId, count, info.getVersion());

        if (i < 1) {
            return false;
        }
        BuyInfo buyInfo = initBuyInfo(productId, count, info);
        buyInfoMapper.insertBuyInfo(buyInfo);
        return true;
    }

    /**
     * 重入乐观锁
     * 购买2200次，库存减少716，剩余库存1284，也就是说失败次数1560次。版本号717,耗时3分27秒
     *
     * @param productId
     * @param count
     * @return
     */
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public Boolean buyProductOptimismLockReentrant(String productId, Integer count) {
        ProductInfo info = null;
        long start = System.currentTimeMillis();
        while (true) {
            long current = System.currentTimeMillis();
            if (current - start > 300L) {
                return false;
            }
            info = productInfoMapper.getProductInfoByProductId(productId);
            if (null == info || count > info.getInventoryCount()) {
                return false;
            }
            int i = productInfoMapper.reduceProductInventoryOptimismLock(productId, count, info.getVersion());
            if (i > 0) {
                break;
            }
        }
        BuyInfo buyInfo = initBuyInfo(productId, count, info);
        buyInfoMapper.insertBuyInfo(buyInfo);
        return true;
    }

    private BuyInfo initBuyInfo(String productId, Integer count, ProductInfo info) {
        BuyInfo buyInfo = new BuyInfo();
        ThreadLocalRandom current = ThreadLocalRandom.current();
        buyInfo.setCount(count);
        buyInfo.setNumber(current.nextInt(100000000, 1000000000));
        buyInfo.setUserId(987654321);
        buyInfo.setProductId(productId);
        BigDecimal total = info.getPrice().multiply(new BigDecimal(count));
        buyInfo.setTotal(total);
        buyInfo.setRate(info.getPrice());
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now.toString());
        buyInfo.setBuyTime(now);
        return buyInfo;
    }
}
